/*
 * Copyright (c) 2020 Carnegie Lee
 * All rights reserved
 */
package com.geekc.zkdistributelock.lock.impl;

import com.geekc.zkdistributelock.constant.DistributeLockConst;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.zookeeper.CreateMode;

import java.util.concurrent.CountDownLatch;

/**
 * 分布式锁的默认实现类 - 基于zk的临时节点
 * @author Carnegie Lee
 */
public class DefaultDistributeLock extends AbstractDistributeLock {

    private final CuratorFramework curatorFramework;

    private static volatile CountDownLatch countDownLatch;

    private final String path;

    public DefaultDistributeLock(CuratorFramework curatorFramework, String lockName) {
        super();
        this.curatorFramework = curatorFramework;
        this.countDownLatch = new CountDownLatch(1);
        this.path = DistributeLockConst.DISTRIBUTE_LOCK_ROOT_PATH + "/" + lockName;
        this.addWatcher();
    }

    @Override
    public final boolean tryLock() {
        boolean isLocked;
        try {
            curatorFramework.create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.EPHEMERAL)
                    .forPath(path);
            isLocked = true;
        } catch (Exception e) {
            isLocked = false;
            try {
                if (this.countDownLatch.getCount() <= 0) {
                    this.countDownLatch = new CountDownLatch(1);
                }
                this.countDownLatch.await();
            } catch (InterruptedException interruptedException) {
                interruptedException.printStackTrace();
            }
        }
        return isLocked;
    }

    @Override
    public final boolean tryRelease() {
        boolean isRelease;
        try {
            if (null != curatorFramework.checkExists().forPath(path)) {
                curatorFramework.delete()
                        .deletingChildrenIfNeeded()
                        .forPath(path);
            }
            isRelease = true;
        } catch (Exception e) {
            isRelease = false;
        }
        return isRelease;
    }

    private void addWatcher() {
        PathChildrenCache cache = new PathChildrenCache(this.curatorFramework, DistributeLockConst.DISTRIBUTE_LOCK_ROOT_PATH, false);
        try {
            cache.getListenable().addListener((client, event) -> {
                if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {
                    this.countDownLatch.countDown();
                }
            });
            cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
